package lol.clann.minecraft.springboot.plugin.core.config.configurate;

import relocate.com.fasterxml.jackson.databind.JsonNode;
import relocate.com.fasterxml.jackson.databind.node.ArrayNode;
import relocate.com.fasterxml.jackson.databind.node.ObjectNode;
import relocate.com.fasterxml.jackson.databind.node.TextNode;
import com.google.auto.service.AutoService;
import lol.clann.minecraft.springboot.api.spi.sbp.ApplicationRunListener;
import lol.clann.minecraft.springboot.plugin.core.config.jackson.Jackson;
import relocate.com.google.common.reflect.TypeToken;
import relocate.ninja.leaping.configurate.ConfigurationNode;
import relocate.ninja.leaping.configurate.objectmapping.ObjectMappingException;
import relocate.ninja.leaping.configurate.objectmapping.serialize.TypeSerializer;
import relocate.ninja.leaping.configurate.objectmapping.serialize.TypeSerializers;
import org.springframework.boot.SpringApplication;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * 让configurate支持JsonNode
 *
 * @author pyz
 * @date 2018/8/13 下午9:53
 */
@AutoService(ApplicationRunListener.class)
public class SerializerJsonNode implements TypeSerializer<JsonNode>, ApplicationRunListener {

    private final TypeToken<JsonNode> jsonNodeTypeToken = TypeToken.of(JsonNode.class);

    @Override
    public void starting(SpringApplication application) {
        TypeSerializers.getDefaultSerializers().registerPredicate(typeToken -> JsonNode.class.isAssignableFrom(typeToken.getRawType()), this);
    }

    @Override
    public JsonNode deserialize(TypeToken<?> type, ConfigurationNode value) throws ObjectMappingException {
        JsonNode json;
        if (value.hasListChildren()) {
            ArrayNode arrayNode = Jackson.getObjectMapper().createArrayNode();
            json = arrayNode;
            for (ConfigurationNode configurationNode : value.getChildrenList()) {
                TypeSerializer<JsonNode> de = value.getOptions().getSerializers().get(jsonNodeTypeToken);
                arrayNode.add(de.deserialize(jsonNodeTypeToken, configurationNode));
            }
        } else if (value.hasMapChildren()) {
            ObjectNode objectNode = Jackson.getObjectMapper().createObjectNode();
            json = objectNode;
            for (Map.Entry<Object, ? extends ConfigurationNode> entry : value.getChildrenMap().entrySet()) {
                String key = (String) entry.getKey();
                ConfigurationNode cn = entry.getValue();
                TypeSerializer<JsonNode> de = value.getOptions().getSerializers().get(jsonNodeTypeToken);
                objectNode.replace(key, de.deserialize(jsonNodeTypeToken, cn));
            }
        } else {
            json = new TextNode(value.getString());
        }
        return json;
    }


    @Override
    public void serialize(TypeToken<?> type, JsonNode obj, ConfigurationNode value) throws ObjectMappingException {
        if (obj instanceof ObjectNode) {
            value.setValue(new HashMap<>());
            Iterator<Map.Entry<String, JsonNode>> it = obj.fields();
            while (it.hasNext()) {
                Map.Entry<String, JsonNode> e = it.next();
                String k = e.getKey();
                JsonNode v = e.getValue();
                TypeSerializer<JsonNode> de = value.getOptions().getSerializers().get(jsonNodeTypeToken);
                de.serialize(jsonNodeTypeToken, v, value.getNode(k));
            }
        } else if (obj instanceof ArrayNode) {
            value.setValue(new ArrayList<>());
            Iterator<JsonNode> it = obj.elements();
            while (it.hasNext()) {
                JsonNode v = it.next();
                TypeSerializer<JsonNode> de = value.getOptions().getSerializers().get(jsonNodeTypeToken);
                de.serialize(jsonNodeTypeToken, v, value.getAppendedNode());
            }
        } else {
            value.setValue(obj.asText());
        }
    }
}
